﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;

namespace Heimdallr.Net
{
    /// <summary> 
    /// An assistant class that provides easy access to properties of HTTP incoming requests. 
    /// </summary>
    public sealed class HttpRequestContext : HttpOperationContext
    {
        #region Field

        private readonly IEnumerable<string> accept;
        private readonly long contentLength;
        private readonly string contentType;
        private readonly NameValueCollection headers;
        private readonly IEnumerable<string> ifMatch;
        private readonly DateTime? ifModifiedSince;
        private readonly DateTime? ifUnmodifiedSince;
        private readonly string method;
        private readonly string userAgent;

        private readonly Stream stream;

        #endregion

        #region Property

        /// <summary>
        /// Gets the Accept header value from the incoming request.
        /// </summary>
        public IEnumerable<string> Accept
        {
            get { return this.accept; }
        }

        /// <summary>
        /// Gets the ContentLength header value of the incoming request.
        /// </summary>
        public long ContentLength
        {
            get { return this.contentLength; }
        }

        /// <summary>
        /// Gets the ContentType header value from the incoming request.
        /// </summary>
        public string ContentType
        {
            get { return this.contentType; }
        }

        /// <summary>
        /// Gets a collection of the items contained in the requests If-Match header.
        /// </summary>
        public IEnumerable<string> IfMatch
        {
            get { return this.ifMatch; }
        }

        /// <summary>
        /// Gets the value of the request's If-Modified-Since header.
        /// </summary>
        public DateTime? IfModifiedSince
        {
            get { return this.ifModifiedSince; }
        }

        /// <summary>
        /// Gets the value of the request's If-Unmatched-Since header.
        /// </summary>
        public DateTime? IfUnmodifiedSince
        {
            get { return this.ifUnmodifiedSince; }
        }

        /// <summary>
        /// Gets the HTTP method of the incoming request.
        /// </summary>
        public string Method
        {
            get { return this.method; }
        }

        /// <summary>
        /// Gets the UserAgent header value from the incoming request.
        /// </summary>
        public string UserAgent
        {
            get { return this.userAgent; }
        }

        /// <summary>
        /// Gets the headers for the incoming request.
        /// </summary>
        public NameValueCollection Headers
        {
            get { return this.headers; }
        }

        #endregion

        #region Internal Constructor

        internal HttpRequestContext(string username, HttpListenerRequest request)
            : base(username, request.RemoteEndPoint, request.Url)
        {
            this.accept = request.AcceptTypes;
            this.contentLength = request.ContentLength64;
            this.contentType = request.ContentType;
            this.headers = new NameValueCollection(request.Headers);

            string ifMatchValue = this.headers["If-Match"];

            if (string.IsNullOrWhiteSpace(ifMatchValue))
                this.ifMatch = new string[0];
            else
                this.ifMatch = ifMatchValue.Split(',').Where(h => !string.IsNullOrWhiteSpace(h)).Select(h => h.Trim().Trim('"'));

            this.ifModifiedSince = GetDateTimeValue(this.headers["If-Modified-Since"]);
            this.ifUnmodifiedSince = GetDateTimeValue(this.headers["If-Unmodified-Since"]);
            this.method = request.HttpMethod;
            this.userAgent = request.UserAgent;

            this.stream = request.InputStream;
        }

        #endregion

        #region Method

        /// <summary>
        /// Gets a stream that contains the body data sent by the client.
        /// </summary>
        /// <returns>A stream that contains the body data sent by the client.</returns>
        /// <exception cref="ObjectDisposedException">Object is disposed.</exception>
        public Stream GetRequestStream()
        {
            if (base.IsDisposed)
                throw new ObjectDisposedException(this.GetType().FullName, "Object is disposed.");

            return this.stream;
        }

        /// <summary>
        /// Disposes this object.
        /// </summary>
        public override void Dispose()
        {
            using (this.stream)
                base.Dispose();
        }

        #endregion
    }
}